D:\a\scloud-dns\scloud-dns\src\dns\packet\answer\mod.rs
Line | Count | Source |
1 | | use crate::dns::q_class::DNSClass; |
2 | | use crate::dns::q_name::parse_qname; |
3 | | use crate::dns::q_type::DNSRecordType; |
4 | | use crate::exceptions::SCloudException; |
5 | | |
6 | | #[derive(Debug, PartialEq)] |
7 | | pub struct AnswerSection { |
8 | | pub q_name: String, |
9 | | pub r_type: DNSRecordType, |
10 | | pub r_class: DNSClass, |
11 | | pub ttl: u32, |
12 | | pub rdlength: u16, |
13 | | pub rdata: Vec<u8>, |
14 | | } |
15 | | |
16 | | impl AnswerSection { |
17 | | /// Serialize AnswerSection into bytes (simple form, no compression) |
18 | | /// |
19 | | /// # Exemple : |
20 | | /// ``` |
21 | | /// use crate::dns::answer::AnswerSection; |
22 | | /// use crate::dns::q_type::DNSRecordType; |
23 | | /// use crate::dns::q_class::DNSClass; |
24 | | /// |
25 | | /// // A Answer for example.com → 93.184.216.34 |
26 | | /// let answer = AnswerSection { |
27 | | /// q_name: "example.com".to_string(), |
28 | | /// r_type: DNSRecordType::A, |
29 | | /// r_class: DNSClass::IN, |
30 | | /// ttl: 300, |
31 | | /// rdlength: 4, |
32 | | /// rdata: vec![93, 184, 216, 34], |
33 | | /// }; |
34 | | /// |
35 | | /// let bytes = answer.to_bytes().unwrap(); |
36 | | /// |
37 | | /// // NAME + TYPE + CLASS + TTL + RDLENGTH + RDATA |
38 | | /// assert!(bytes.len() > 12); |
39 | | /// ``` |
40 | 3 | pub fn to_bytes(&self) -> Result<Vec<u8>, SCloudException> { |
41 | 3 | let mut buf = Vec::new(); |
42 | | |
43 | | // Encode NAME |
44 | 5 | for label in self.q_name.split('.')3 { |
45 | 5 | let len = label.len(); |
46 | 5 | if len > 63 { |
47 | 2 | return Err(SCloudException::SCLOUD_ANSWER_DESERIALIZATION_FAILED_LABEL_TOO_LONG); |
48 | 3 | } |
49 | 3 | buf.push(len as u8); |
50 | 3 | buf.extend_from_slice(label.as_bytes()); |
51 | | } |
52 | 1 | buf.push(0x00); |
53 | | |
54 | 1 | let rtype_u16 = |
55 | 1 | u16::try_from(self.r_type).expect("Cannot convert AnswerSection q_type to u16"); |
56 | 1 | buf.extend_from_slice(&rtype_u16.to_be_bytes()); |
57 | | |
58 | 1 | let rclass_u16 = u16::try_from(self.r_class).unwrap(); |
59 | 1 | buf.extend_from_slice(&rclass_u16.to_be_bytes()); |
60 | | |
61 | 1 | buf.extend_from_slice(&self.ttl.to_be_bytes()); |
62 | 1 | buf.extend_from_slice(&self.rdlength.to_be_bytes()); |
63 | 1 | buf.extend_from_slice(&self.rdata); |
64 | | |
65 | 1 | Ok(buf) |
66 | 3 | } |
67 | | |
68 | | /// Deserialize one AnswerSection and return (section, consumed_bytes) |
69 | | /// |
70 | | /// # Exemple : |
71 | | /// ``` |
72 | | /// use crate::dns::answer::AnswerSection; |
73 | | /// use crate::dns::q_type::DNSRecordType; |
74 | | /// use crate::dns::q_class::DNSClass; |
75 | | /// |
76 | | /// // example.com A 93.184.216.34 |
77 | | /// let raw_answer: Vec<u8> = vec![ |
78 | | /// 0x07, b'e', b'x', b'a', b'm', b'p', b'l', b'e', |
79 | | /// 0x03, b'c', b'o', b'm', |
80 | | /// 0x00, // End of NAME |
81 | | /// 0x00, 0x01, // TYPE = A |
82 | | /// 0x00, 0x01, // CLASS = IN |
83 | | /// 0x00, 0x00, 0x01, 0x2c, // TTL = 300 |
84 | | /// 0x00, 0x04, // RDLENGTH = 4 |
85 | | /// 93, 184, 216, 34, // RDATA |
86 | | /// ]; |
87 | | /// |
88 | | /// let (answer, consumed) = AnswerSection::from_bytes(&raw_answer, 0).unwrap(); |
89 | | /// |
90 | | /// assert_eq!(answer.q_name, "example.com"); |
91 | | /// assert_eq!(answer.r_type, DNSRecordType::A); |
92 | | /// assert_eq!(answer.r_class, DNSClass::IN); |
93 | | /// assert_eq!(answer.ttl, 300); |
94 | | /// assert_eq!(answer.rdata, vec![93, 184, 216, 34]); |
95 | | /// assert_eq!(consumed, raw_answer.len()); |
96 | | /// ``` |
97 | 3 | pub(crate) fn from_bytes( |
98 | 3 | buf: &[u8], |
99 | 3 | offset: usize, |
100 | 3 | ) -> Result<(AnswerSection, usize), SCloudException> { |
101 | 3 | let (q_name, mut pos) = parse_qname(buf, offset).unwrap(); |
102 | | |
103 | 3 | if pos + 10 > buf.len() { |
104 | 1 | return Err(SCloudException::SCLOUD_IMPOSSIBLE_PARSE_ANSWER_HEADER_TOO_SHORT); |
105 | 2 | } |
106 | | |
107 | 2 | let r_type = DNSRecordType::try_from(u16::from_be_bytes([buf[pos], buf[pos + 1]])).unwrap(); |
108 | 2 | pos += 2; |
109 | | |
110 | 2 | let r_class = DNSClass::try_from(u16::from_be_bytes([buf[pos], buf[pos + 1]])).unwrap(); |
111 | 2 | pos += 2; |
112 | | |
113 | 2 | let ttl = u32::from_be_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]); |
114 | 2 | pos += 4; |
115 | | |
116 | 2 | let rdlength = u16::from_be_bytes([buf[pos], buf[pos + 1]]); |
117 | 2 | pos += 2; |
118 | | |
119 | 2 | if pos + rdlength as usize > buf.len() { |
120 | 1 | return Err(SCloudException::SCLOUD_IMPOSSIBLE_PARSE_ANSWER_RDATA_OUT_OF_BOUNDS); |
121 | 1 | } |
122 | 1 | let rdata = buf[pos..pos + rdlength as usize].to_vec(); |
123 | 1 | pos += rdlength as usize; |
124 | | |
125 | 1 | Ok(( |
126 | 1 | AnswerSection { |
127 | 1 | q_name, |
128 | 1 | r_type, |
129 | 1 | r_class, |
130 | 1 | ttl, |
131 | 1 | rdlength, |
132 | 1 | rdata, |
133 | 1 | }, |
134 | 1 | pos - offset, |
135 | 1 | )) |
136 | 3 | } |
137 | | } |